// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PACKET_QUEUE_H
#define PACKET_QUEUE_H

#include <condition_variable>
#include <deque>
#include <mutex>

class PacketQueue {
public:
    /**
     * @brief: construct
     * @param [in] multiEnq: multiEnq
     * @param [in] multiDeq: multiDeq
     * @param [in] isBlock: queue is blocked
     */
    PacketQueue(bool multiEnque, bool multiDeque, bool isBlock);

    /**
    * @brief: deconstruct
    */
    ~PacketQueue();

    /**
     * @brief: put data into data queue
     * @param [in] packet: data
     */
    void PutPkt(const std::pair<uint8_t *, uint32_t> & packet);

    /**
     * @brief: get a data from queue
     * @return : a pair that consist of by pointer and size of data packet,
     *           or a pair consist of nullptr and 0 if there are no data packet in the queue
     */
    std::pair<uint8_t *, uint32_t> GetNextPkt();

    /**
     * @brief: get a data from queue with timeout
     * @param [in] timeoutMillis: unit is ms
     * @return : a pair that consist of by pointer and size of data packet,
     *           or a pair consist of nullptr and 0 if there are no data packet in the queue
     */
    std::pair<uint8_t *, uint32_t> GetNextPktWait(int timeoutMillis);

    /**
     * @brief: get queue size
     * @return : number of items in the queue
     */
    size_t GetNumItems();

    /**
     * @brief: get total size
     * @return : total data size
     */
    uint32_t GetTotalSize();

    /**
     * @brief: If you want to release the communication object between threads, you should
     *         call this interface first, especially in blocking mode, and release the object
     *         after the blocked receiving thread exits.
     */
    void Cancel();

    /**
     * @brief: resume running
     */
    void Continue();

    /**
     * @brief: clear queue and free memory
     */
    void Clear();

private:
    uint32_t m_totalDataSize = 0;
    std::deque<std::pair<uint8_t *, uint32_t>> m_packetDeque = {};
    std::mutex m_lock = {};
    std::condition_variable m_conditionVariable = {};
    bool m_multiEnq;
    bool m_multiDeq;
    bool m_block;
    volatile bool m_status;
};

#endif
